﻿#pragma once

#include "Framework/Application.h"
#include "Widgets/Window.h"

#include "Scene/Scene.h"
#include "Scene/SceneNode.h"
#include "Scene/CameraComponent.h"
#include "Scene/DirectionalLightComponent.h"
#include "Scene/StaticMeshComponent.h"
#include "Scene/SkyBoxComponent.h"
#include "Scene/AmbientLightComponent.h"
#include "Scene/PointLightComponent.h"
#include "Scene/SpotLightComponent.h"

#include "Math/Math.h"
#include "Asset/EngineDefaultPackage.h"

#include "Widgets/ViewportWidget.h"
#include "Widgets/RootWidget.h"
#include "Widgets/Window.h"
#include "Widgets/Mouse/MouseWidget.h"
#include "Widgets/ButtonWidget.h"

#include "Material/MaterialSubsystem.h"

static TSharedObjectPtr<RMaterial> CreateTestDynamiacParameterMaterial()
{
    TSharedObjectPtr<RShader> FragmentShader;
    {
        TVector<uint8_t> ShaderData={
            #include "Shaders/TestDynamicParameter.frag.spv.h"
        };
        FragmentShader=RShader::CreateShader(ShaderData,EShaderStage::Fragment);
    }

    RMaterial::SMaterialCreateInfo CreateInfo;
    CreateInfo.VertexShader=REngineDefaultPackage::Get()->GetDefaultMeshVertexShader();
    CreateInfo.FragmentShader=FragmentShader;
    CreateInfo.PipelineType=ERenderMaterialType::Opaque;
    CreateInfo.FloatParameters={0.2f,0.3f,0.4f};
    CreateInfo.ParameterDescriptions.Add(CString(U"颜色"),SMaterialParameterDescription(EMaterialParameterType::Vector3,0));

    TSharedObjectPtr<RMaterial> Material=RMaterial::Create(
        CreateInfo
    );

    return Material;
}

TSharedObjectPtr<RScene> CreateScene(TSharedObjectPtr<RMaterial> InMaterial)
{
    RScenePtr Scene=NewObject<RScene>();
    {
        auto Light=NewObject<RSceneNode>();
        Scene->GetRootNode()->AddSubNode(Light);
        Light->SetNodeName(U"DirectionalLight");
        Light->SetWorldPosition(SVec3(0,0,0)); //any where is ok
        Light->SetWorldRotation(SQuat::FromEuler(SVec3(CMathBase::DegreeToRadian(-45.f),0,0)));
        {
            auto LightComponent=Light->CreateAndAddComponent<RDirectionalLightComponent>();
            LightComponent->DistanceToTarget=10;
        }

        // auto PointLight=NewObject<RSceneNode>();
        // Scene->GetRootNode()->AddSubNode(PointLight);
        // PointLight->SetNodeName(U"PointLight");
        // PointLight->SetWorldPosition(SVec3(0,0,3));
        // {
        //     auto LightComponent=PointLight->CreateAndAddComponent<RPointLightComponent>();
        //     LightComponent->Color=SColor::Yellow;
        //     LightComponent->Radius=4;
        // }

        // auto SpotLight=NewObject<RSceneNode>();
        // Scene->GetRootNode()->AddSubNode(SpotLight);
        // SpotLight->SetNodeName(U"SpotLight");
        // SpotLight->SetWorldPosition(SVec3(0,0,3));
        // SpotLight->SetWorldRotation(SQuat::FromEuler(SVec3(
        //     CMathBase::DegreeToRadian(-90.f)
        //     ,0
        //     ,0)));
        // {
        //     auto LightComponent=SpotLight->CreateAndAddComponent<RSpotLightComponent>();
        //     LightComponent->Color=SColor::Red;
        // }

        // auto AmbientLight=NewObject<RSceneNode>();
        // Scene->GetRootNode()->AddSubNode(AmbientLight);
        // AmbientLight->SetNodeName(U"AmbientLight");
        // {
        //     auto LightComponent=AmbientLight->CreateAndAddComponent<RAmbientLightComponent>();
        // }

        // auto GreyFloor=NewObject<RSceneNode>();
        // Scene->GetRootNode()->AddSubNode(GreyFloor);
        // GreyFloor->SetNodeName(U"GreyFloor");
        // GreyFloor->SetWorldPosition(SVec3(0,0,-1));
        // GreyFloor->SetScale(SVec3(10,10,1));
        // {
        //     auto MeshComponent=GreyFloor->CreateAndAddComponent<RStaticMeshComponent>();
        //     MeshComponent->SetStaticMesh(REngineDefaultPackage::Get()->GetCubeMesh());
        //     MeshComponent->SetMaterials({REngineDefaultPackage::Get()->GetDefaultMaterial()});
        // }

        // auto MeshNode=NewObject<RSceneNode>();
        // Scene->GetRootNode()->AddSubNode(MeshNode);
        // MeshNode->SetNodeName(U"Mesh");
        // MeshNode->SetWorldPosition(SVec3(0,0,1));
        // MeshNode->SetWorldRotation(SQuat::FromEuler(SVec3(0,0,0)));
        // {
        //     //默认材质
        //     auto DefaultMeshComponent=MeshNode->CreateAndAddComponent<RStaticMeshComponent>();
        //     DefaultMeshComponent->SetStaticMesh(REngineDefaultPackage::Get()->GetSphereMesh());
        //     DefaultMeshComponent->SetMaterials({InMaterial});
        // }

        // auto SeflLight=NewObject<RSceneNode>();
        // Scene->GetRootNode()->AddSubNode(SeflLight);
        // SeflLight->SetNodeName(U"SelfLight");
        // SeflLight->SetWorldPosition(SVec3(-1,0,1));
        // SeflLight->SetWorldRotation(SQuat::FromEuler(SVec3(0,0,0)));
        // {
        //     auto PreviewMeshComponent=SeflLight->CreateAndAddComponent<RStaticMeshComponent>();
        //     PreviewMeshComponent->SetStaticMesh(REngineDefaultPackage::Get()->GetSphereMesh());
            
        //     RMaterial::SMaterialCreateInfo CreateInfo;
        //     CreateInfo.VertexShader=REngineDefaultPackage::Get()->GetDefaultMeshVertexShader();
        //     CreateInfo.FragmentShader=REngineDefaultPackage::Get()->GetDefaultSelfLightingFragmentShader();
        //     CreateInfo.PipelineType=ERenderMaterialType::Unlit;

        //     auto Material=RMaterial::Create(CreateInfo);
            
        //     PreviewMeshComponent->SetMaterials({Material});
        // }

        //auto TestDynamicParameterMaterial=CreateTestDynamiacParameterMaterial();

        // auto TestDynamicParameter=NewObject<RSceneNode>();
        // Scene->GetRootNode()->AddSubNode(TestDynamicParameter);
        // TestDynamicParameter->SetNodeName(U"TestDynamicParameter");
        // TestDynamicParameter->SetWorldPosition(SVec3(1,0,0));
        // TestDynamicParameter->SetWorldRotation(SQuat::FromEuler(SVec3(0,0,0)));
        // {
        //     auto PreviewMeshComponent=TestDynamicParameter->CreateAndAddComponent<RStaticMeshComponent>();
        //     PreviewMeshComponent->SetStaticMesh(REngineDefaultPackage::Get()->GetSphereMesh());
        //     PreviewMeshComponent->SetMaterials({TestDynamicParameterMaterial});
        // }
        
        // auto TestDynamicParameterMaterial2= 
        //     RMaterialSubsystem::GetInstance()->CopyMaterial(TestDynamicParameterMaterial);
        // TestDynamicParameterMaterial2->SetVector3Parameter(U"颜色",SVec3(0,1,0));
        
        // auto TestDynamicParameter2=NewObject<RSceneNode>();
        // Scene->GetRootNode()->AddSubNode(TestDynamicParameter2);
        // TestDynamicParameter2->SetNodeName(U"TestDynamicParameter2");
        // TestDynamicParameter2->SetWorldPosition(SVec3(2,0,0));
        // TestDynamicParameter2->SetWorldRotation(SQuat::FromEuler(SVec3(0,0,0)));
        // {
        //     auto PreviewMeshComponent=TestDynamicParameter2->CreateAndAddComponent<RStaticMeshComponent>();
        //     PreviewMeshComponent->SetStaticMesh(REngineDefaultPackage::Get()->GetSphereMesh());
        //     PreviewMeshComponent->SetMaterials({TestDynamicParameterMaterial2});
        // }

        auto SkyBox=NewObject<RSceneNode>();
        Scene->GetRootNode()->AddSubNode(SkyBox);
        SkyBox->SetNodeName(U"SkyBox");
        SkyBox->SetWorldPosition(SVec3(0,0,0));
        SkyBox->SetWorldRotation(SQuat::FromEuler(SVec3(0,0,0)));
        {
            auto SkyBoxComponent=SkyBox->CreateAndAddComponent<RSkyBoxComponent>();
        }

        //测试网格合批功能，网格和材质都相同的网格将被合并渲染
        //目前合批效果有待提高，500个球体合批后，帧率下降到20fps
        for(int32_t MeshIndex=0;MeshIndex<500;MeshIndex++)
        {
            auto PreviewMesh=NewObject<RSceneNode>();
            Scene->GetRootNode()->AddSubNode(PreviewMesh);
            PreviewMesh->SetNodeName(U"Batch");
            static int Row=0;
            static int Column=0;
            PreviewMesh->SetWorldPosition(SVec3(Column*2,Row*2,2));
            Column++;
            if(Column>100)
            {
                Column=0;
                Row++;
            }
            
            auto PreviewMeshComponent=PreviewMesh->CreateAndAddComponent<RStaticMeshComponent>();
            PreviewMeshComponent->SetStaticMesh(REngineDefaultPackage::Get()->GetSphereMesh());
            PreviewMeshComponent->SetMaterials({InMaterial});
            
        }


        Scene->BeginPlay();    
    }

    return Scene;
}

int main(int argc, char* argv[])
{
    auto App=NewObject<RApplication>();

    SApplicationArgument Argument;
    Argument.ExecutablePath=argv[0];
    Argument.FallBackFont=Argument.ExecutablePath.Parent()/U"Content"/U"EngineEditor2"/U"Fonts"/U"SourceHanSansHWSC-Bold.otf";
    Argument.DefaultFont=Argument.FallBackFont;
    App->Initialize(Argument);
    App->EnablePrintFPS();

    auto Window=NewObject<RWindow>();
    Window->SetWindowSize(SVec2(800,600));
    Window->SetWindowPosition(SVec2(100,100));
    Window->Show();

    auto PreviewViewport=NewWidget<RViewportWidget>(Window->GetRootWidget());
    PreviewViewport->Anchor.Fill(Window->GetRootWidget());

    auto Material=CreateTestDynamiacParameterMaterial();

    auto Scene=CreateScene(Material);
    PreviewViewport->SetScene(Scene);


    PreviewViewport->bForceUseDefaultParameters=true;

    //calculate the position and rotation of the camera
    static const float DistanceToPreviewMesh=10.0f;
    static float Pitch=0;// CMathBase::DegreeToRadian(-45.f);
    static float Yaw=0;

    
    RViewportWidgetWeakPtr PreviewViewportWeak=PreviewViewport;
    PreviewViewport->ViewportRotation=[]()->SQuat
    {
        return SQuat::FromEuler(SVec3(Pitch,0,Yaw));
    };

    PreviewViewport->ViewportPosition=[PreviewViewportWeak]()->SVec3
    {
        return PreviewViewportWeak->ViewportRotation.Get().GetForwardDirection()*-DistanceToPreviewMesh;
    };


    //create a widget to let user to rotate view
    auto Mouse =NewWidget<RMouseWidget>(Window->GetRootWidget());
    Mouse->Anchor.Fill(Window->GetRootWidget());
    Mouse->Drag->bIsEnabled=true;

    //you cant drag the mouse on two widgets at the same time
    //so use a static variable is safe
    static float InitPitch=0;
    static float InitYaw=0;

    Mouse->Drag->OnDragStarted.Bind([]()
    {
        InitPitch=Pitch;
        InitYaw=Yaw;
    });

    Mouse->Drag->OnDragMoved.Bind([PreviewViewportWeak](const SVec2& MouseDelta )
    {
        float DeltaPitch= -MouseDelta.Y*0.01f;
        Pitch=CMath::Clamp(InitPitch+DeltaPitch, -CMathBase::HalfPi,CMathBase::HalfPi);
        Yaw=InitYaw-MouseDelta.X*0.01f;
    });


    auto Button=NewWidget<RButtonWidget>(Window->GetRootWidget());
    Button->X=0;
    Button->Y=0;
    Button->Width=100;
    Button->Height=20;

    Button->Text=U"改变颜色";
    Button->TextStyle.Color=SColor::Green;
    auto WeakButton=TWeakObjectPtr<RButtonWidget>(Button);
    Button->BackgroundStyle.FillColor=([WeakButton]()->SColor{
        if(WeakButton->IsHovered())
        {
            return SColor::Red;
        }
        else
        {
            return SColor::Blue;
        }
    });

    Button->OnClicked.BindLambda([WeakButton,Material](const SMouseButtonEvent &InEvent)->bool{
        
        static SVec3 Color=SVec3(0,1,0);
        Color.X+=0.1f;
        if(Color.X>1)
        {
            Color.X=0;
        }
        Material->SetVector3Parameter(U"颜色",Color);
        return true;
    });


    return App->Run();
}